#include "MsgWindow.h"
#include "WMShatter.h"
#include <process.h>

#define WM_REQCREATENEWWND        WM_USER+0x0512
#define MSGLOOPWINDOWREGCLSNAME L"MsgLoopWindowDefaultRegClass_"

class IProcessCreateNewWindow
{
public:
    IProcessCreateNewWindow() {}
    virtual ~IProcessCreateNewWindow(){}

    virtual LRESULT ProcessCreateNewWindow(WPARAM wParam, LPARAM lParam) = 0;
};

class CMsgCreateWindowProxy : public IProcessWndMessage
{
public:
    CMsgCreateWindowProxy() : m_pProcessCreateNewWindow(NULL) {}
    ~CMsgCreateWindowProxy() { m_pProcessCreateNewWindow = NULL; }

    void SetCreateNewWindowInterfacePtr(IProcessCreateNewWindow* pProcessCreateNewWindow)
    {
        m_pProcessCreateNewWindow = pProcessCreateNewWindow;
    }

    LRESULT ProcessWndMessage(UINT msg, WPARAM wParam, LPARAM lParam) override
    {
        if (msg == WM_REQCREATENEWWND && m_pProcessCreateNewWindow)
        {
            return m_pProcessCreateNewWindow->ProcessCreateNewWindow(wParam, lParam);
        }
        return 0;
    }

private:
    IProcessCreateNewWindow* m_pProcessCreateNewWindow;
};

class CMsgWindowClass : public IProcessCreateNewWindow
{
public:
    CMsgWindowClass();
    ~CMsgWindowClass();

    bool StartMsgLoop();

    bool StopMsgLoop();

    HWND NewMsgWindow(const std::wstring& strWindowName, IProcessWndMessage* pMsgWindow);

    static unsigned _stdcall UIMsgLoopThread(LPVOID);

    static LRESULT CALLBACK MsgWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);

    LRESULT ProcessCreateNewWindow(WPARAM wParam, LPARAM lParam) override;

private:
    CMsgCreateWindowProxy m_CreateWndProxy;
    ATOM            m_ClassAtom;
    HINSTANCE        m_Instance;
    HWND            m_hCreateWndProxy;
    bool            m_bInit;
    unsigned int    m_uiTid;
    HANDLE            m_hThread;
    HANDLE            m_ThreadCreatedEvent;
};

CMsgWindowClass::CMsgWindowClass() : 
      m_ClassAtom(0)
    , m_Instance(GetModuleHandle(NULL))
    , m_hCreateWndProxy(NULL)
    , m_bInit(false)
    , m_uiTid(0)
    , m_hThread(NULL)
{
    m_ThreadCreatedEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
    m_CreateWndProxy.SetCreateNewWindowInterfacePtr(this);
}

CMsgWindowClass::~CMsgWindowClass()
{
    StopMsgLoop();
    m_ThreadCreatedEvent = NULL;
}

bool CMsgWindowClass::StartMsgLoop()
{
    do
    {
        if (m_bInit)
            break;

        ResetEvent(m_ThreadCreatedEvent);
        m_hThread = (HANDLE)_beginthreadex(NULL, 0,  // 全局鼠标钩子需要独立线程  不阻塞消息循环
            CMsgWindowClass::UIMsgLoopThread, this, 0, &m_uiTid);

        if (WAIT_TIMEOUT == WaitForSingleObject(m_ThreadCreatedEvent, INFINITE/*500*/))
        {
            break;
        }
    } while (FALSE);
    return m_bInit;
}

bool CMsgWindowClass::StopMsgLoop()
{
    if (!m_bInit)
        return false;

    ::PostThreadMessage(m_uiTid, WM_QUIT, 0, 0);
    if (m_hCreateWndProxy)
    {
        ::PostMessage(m_hCreateWndProxy, WM_QUIT, 0, 0);  // exit thread
        if (::IsWindow(m_hCreateWndProxy))
        {
            ::DestroyWindow(m_hCreateWndProxy);
        }
        m_hCreateWndProxy = NULL;
    }
    if (m_hThread)
    {
        if (WAIT_TIMEOUT == WaitForSingleObject(m_hThread, 2000))
        {
            TerminateThread(m_hThread, -1);
        }
        CloseHandle(m_hThread);
        m_hThread = NULL;
    }
    m_uiTid = 0;

    if (m_ClassAtom)
    {
        UnregisterClass((const TCHAR*)m_ClassAtom, m_Instance);
        m_ClassAtom = NULL;
    }
    m_bInit = false;
    return true;
}

LRESULT CALLBACK CMsgWindowClass::MsgWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    LRESULT result = 0;
    if (msg == WM_CREATE)
    {
        SetWindowLongPtrW(wnd, GWLP_USERDATA, (LONG_PTR)(((CREATESTRUCT*)lParam)->lpCreateParams));
    }
    else if (msg == WM_DESTROY)
    {
        SetWindowLongPtr(wnd, GWLP_USERDATA, NULL);
    }

    IProcessWndMessage* _this = (IProcessWndMessage*)GetWindowLongPtr(wnd, GWLP_USERDATA);
    if (!_this)
    {
        return WMShatter::SafeDefWindowProc(wnd, msg, wParam, lParam);
    }

    try
    {
        result = _this->ProcessWndMessage(msg, wParam, lParam);
        _this->AfterProcessWndMessage(msg, wParam, lParam);
    }
    catch (...)
    {
    }
    return result;
};

HWND CMsgWindowClass::NewMsgWindow(const std::wstring& strWindowName, IProcessWndMessage* pMsgWindow)
{
    if (!m_hCreateWndProxy)
        return NULL;
    HWND h = (HWND)::SendMessageW(m_hCreateWndProxy, WM_REQCREATENEWWND, (WPARAM)strWindowName.c_str(), (LPARAM)pMsgWindow);
    return h;
}

LRESULT CMsgWindowClass::ProcessCreateNewWindow(WPARAM wParam, LPARAM lParam)
{
    HWND hWnd = ::CreateWindowExW(0, (LPCWSTR)m_ClassAtom, (LPCWSTR)wParam, 0, 0, 0, 1, 1,
        HWND_MESSAGE, NULL, m_Instance, (LPVOID)lParam);
    return (LRESULT)hWnd;
}

unsigned _stdcall CMsgWindowClass::UIMsgLoopThread(LPVOID pThis)
{
    WCHAR szClsName[100] = { 0 };
    DWORD dwSessionId = 0;
    ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId);
    wsprintf(szClsName, MSGLOOPWINDOWREGCLSNAME L"%d", dwSessionId);

    WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
    wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    wcex.lpfnWndProc = CMsgWindowClass::MsgWindowProc; // 第一个处理函数
    wcex.hInstance = GetModuleHandle(NULL);
    wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszClassName = szClsName;
    ATOM atom = ::RegisterClassEx(&wcex);
    
    HWND hWnd = ::CreateWindowExW(0, (LPCWSTR)atom, L"MsgWindowClassDefaultWindow", 0, 0, 0, 1, 1,
        HWND_MESSAGE, NULL, GetModuleHandle(NULL), &((CMsgWindowClass*)pThis)->m_CreateWndProxy);

    if (pThis)
    {
        ((CMsgWindowClass*)pThis)->m_Instance = GetModuleHandle(NULL);
        ((CMsgWindowClass*)pThis)->m_hCreateWndProxy = hWnd;
        ((CMsgWindowClass*)pThis)->m_ClassAtom = atom;
        ((CMsgWindowClass*)pThis)->m_bInit = true;
        SetEvent(((CMsgWindowClass*)pThis)->m_ThreadCreatedEvent);
    }

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

long CMsgWindow::ms_lMsgWndCount = 0;
CMsgWindowClass* CMsgWindow::ms_MsgWndCls = NULL;
CMsgWindow::CMsgWindow(const std::wstring& _name, bool bRecSameSvrNameMsg) 
    : m_handle(NULL)
    , m_bRecSameSvrNameMsg(bRecSameSvrNameMsg)
{
    if (!ms_MsgWndCls)
    {
        ms_MsgWndCls = new CMsgWindowClass();
        ms_MsgWndCls->StartMsgLoop();
    }
    m_handle = ms_MsgWndCls->NewMsgWindow(_name, this);
    m_strWndName = _name;
    ::InterlockedIncrement(&ms_lMsgWndCount);
}

CMsgWindow::~CMsgWindow()
{
    if (m_handle)
    {
        DestroyWindow(m_handle);
        m_handle = NULL;
    }
    int r = ::InterlockedDecrement(&ms_lMsgWndCount);
    if (r == 0)
    { // final release
        ms_MsgWndCls->StopMsgLoop();
        delete ms_MsgWndCls;
        ms_MsgWndCls = NULL;
    }
}

LPCTSTR CMsgWindow::GetWndName() 
{ 
    return m_strWndName.c_str(); 
}

HWND CMsgWindow::GetHandle() const 
{ 
    return m_handle; 
}

LRESULT
CMsgWindow::AfterProcessWndMessage(UINT msg, WPARAM wParam, LPARAM lParam)
{
    LPCTSTR lpWndName = GetWndName();
    if (!lpWndName || wcslen(lpWndName) <= 0)
    {
        return S_FALSE;
    }
    CMsgWindow* p = GetHeadNode();
    for (; p != NULL; p = p->GetNextNode()) {
        if (p != this && p->m_bRecSameSvrNameMsg) {
            LPCTSTR lpName = p->GetWndName();
            if (lpName && 0 == wcscmp(lpWndName, lpName))
            {// 相同窗口名字  会产生窗口覆盖 Findwindow只会找到后面创建的窗口
                p->ProcessWndMessage(msg, wParam, lParam);
            }
        }
    }
    return S_OK;
}

LRESULT
CMsgWindow::ProcessWndMessage(UINT msg, WPARAM wParam, LPARAM lParam)
{
    return WMShatter::SafeDefWindowProc(GetHandle(), msg, wParam, lParam);
}
